package cn.ac.ict.fpevaluation.cluster.ap;

import cn.ac.ict.fpevaluation.bean.APBean;
import cn.ac.ict.fpevaluation.bean.AtomTrajectoryBean;
import cn.ac.ict.fpevaluation.bean.BeanUtils;
import cn.ac.ict.fpevaluation.bean.FusionBean;
import cn.ac.ict.fpevaluation.fastdtw.dtw.TimeWarpInfo;
import cn.ac.ict.fpevaluation.fastdtw.test.FastDTWTest;
import cn.ac.ict.fpevaluation.fastdtw.timeseries.TimeSeriesPoint;
import cn.ac.ict.fpevaluation.fusion.Fusion;
import cn.ac.ict.fpevaluation.utils.DataOperationToDiskUtils;
import cn.ac.ict.fpevaluation.utils.WifiHelper;

import java.util.*;


public class WifiDistance {

    public void launch() {
        List<AtomTrajectoryBean> atomTrajectories = DataOperationToDiskUtils.getOriginalData();
        List<FusionBean> fusions = new ArrayList<>();
        //读取母本数据
        List<FusionBean> female = DataOperationToDiskUtils.readFusionResult();
        atomTrajectories.forEach(item -> {
            fusions.add(BeanUtils.atomTrajectoryBeanToFusionBean(item));
        });

        int count = female.size() * fusions.size();
        double sum = 0;
        for (int i = 0; i < female.size(); i++) {
            for (int j = 0; j < fusions.size(); j++) {
                double distance = calDtw(female.get(i).getAps(), fusions.get(j).getAps());
                sum += distance;
                System.out.println("两条轨迹之间的DTW distance为: " + distance);

            }
        }
        System.out.printf("平均DTW Distance为: " + sum / count);
    }


    private double calDtw(List<List<APBean>> list1, List<List<APBean>> list2) {
        double[][] dtw = new double[list1.size() + 1][list2.size() + 1];
        int n = list1.size();
        int m = list2.size();
        for (int i = 1; i < n; i++) {
            dtw[i][0] = Integer.MAX_VALUE;
        }

        for (int i = 1; i < m; i++) {
            dtw[0][i] = Integer.MAX_VALUE;
        }
        dtw[0][0] = 0;

        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
//                double cost = subDtwDistance(list1.get(i), list2.get(j));
                double cost = euclideanDistanceAfterRemedy(list1.get(i), list2.get(j));
                //System.out.printf("cost : " + cost + "\n");
                dtw[i][j] = cost + Math.min(dtw[i - 1][j - 1], Math.min(dtw[i - 1][j], dtw[i][j - 1]));
            }
        }

        return dtw[n - 1][m - 1];

    }

    private double euclideanDistanceAfterRemedy(List<APBean> l1, List<APBean> l2) {
        List<APBean> list1 = new ArrayList<>(l1);
        List<APBean> list2 = new ArrayList<>(l2);
        WifiHelper.remedyRssByMac(list1, list2);
        WifiHelper.remedyRssByMac(list2, list1);
        WifiHelper.sortByMac(list1);
        WifiHelper.sortByMac(list2);
        return euclideanDistance(list1, list2);
    }


    private double subDtwDistance(List<APBean> list1, List<APBean> list2) {
        WifiHelper.sortByMac(list1);
        WifiHelper.sortByMac(list2);
        TimeWarpInfo t = new FastDTWTest().calDistance(gettsArray(list1), gettsArray(list2), 3);

        return t.getDistance();
    }

    private static ArrayList<Object> gettsArray(List<?> samplelist) {
        ArrayList<Object> tsArray = new ArrayList<Object>();

        for (int i = 0; i < samplelist.size(); i++) {
            final ArrayList<Double> currentLineValues = new ArrayList<Double>();
            Object sample = samplelist.get(i);
            if (sample instanceof APBean) {
                currentLineValues.add(((APBean) sample).getRSS());
            }

            final TimeSeriesPoint readings = new TimeSeriesPoint(currentLineValues.subList(0, currentLineValues.size()));

            tsArray.add(readings);
        }
        return tsArray;
    }

    private double euclideanDistance(List<APBean> list1, List<APBean> list2) {
        int distance = 0;
        for (int i = 0; i < list1.size(); i++) {
            APBean aBean = list1.get(i);

            APBean bBean = list2.get(i);
            if (aBean.getMAC().equals(bBean.getMAC())) {
                distance += Math.pow(aBean.getRSS() - bBean.getRSS(), 2.0);
                i++;
            }

        }
        return Math.sqrt(distance);
    }


}